/**
 * @file sendq.c
 * Send queue implementation.
 */

/* Created by Laszlo Ashin <kodest@gmail.com> in 2oo8. */

#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>

#include "sendq.h"
#include "utils.h"

void
init_sendq(sendq_t *this)
{
	this->size = 0;
	this->data_length = 0;
	this->buffer = NULL;
}

void
free_sendq(sendq_t *this)
{
	if (this->buffer != NULL) {
		free(this->buffer);
	}
	this->size = 0;
	this->data_length = 0;
}

void
sendq_put(sendq_t *this, const char *data, size_t length)
{
	if (this->data_length + length > this->size) {
		/* Increase buffer size. */
		size_t new_size = this->data_length + length;
		char *new_buffer;

		if (new_size > MAX_SENDQ_SIZE) {
			show_error("Size of buffer of sendq cannot be "
				"increased to %d.", new_size);
			return;
		}
		new_buffer = realloc(this->buffer, new_size);
		if (new_buffer == NULL) {
			show_error("Unable to increase sendq buffer size from "
				"%d to %d", this->size, new_size);
			return;
		}
		this->buffer = new_buffer;
		this->size = new_size;
	}

	memcpy(this->buffer + this->data_length, data, length);
	this->data_length += length;
}

/**
 * Consume data from the sendq. Call this right after the head of the buffer
 * has been processed.
 * @param this Pointer to sendq.
 * @param length Length of the processed data.
 */
static void
sendq_consume(sendq_t *this, size_t length)
{
	if (length > this->data_length) {
		show_error("Unable to consume %d bytes from a sendq with an "
			"actual length of data of %d bytes.", length,
			this->data_length);
		return;
	}

	memmove(this->buffer, this->buffer + length, this->data_length-length);
	this->data_length -= length;

	if ((this->data_length < this->size / 2) &&
	    (this->size / 2 >= MIN_SENDQ_SIZE)) {
		size_t new_size = this->size / 2;
		char *new_buffer;

		new_buffer = realloc(this->buffer, new_size);
		if (new_buffer != NULL) {
			this->buffer = new_buffer;
			this->size = new_size;
		}
	}
}

ssize_t
sendq_send(sendq_t *this, int fd)
{
	ssize_t sent = send(fd, this->buffer, this->data_length, 0);

	if (sent > 0) {
		sendq_consume(this, (size_t)sent);
	}
	return sent;
}
